package ngat.oss;

import ngat.message.OSS.*;
import ngat.util.logging.*;
import ngat.phase2.*; 
import ngat.instrument.*;
import ngat.phase2.nonpersist.*;

import java.util.*;
import java.io.*;

import jyd.dbms.*;
import jyd.storable.*;
import jyd.collection.*;

/**
 * Action: Sent to retreive Proposal for editing. If the dolock parameter is set then
 * he Proposal is locked for exclusive use by this user until they submit changes. 
 * No scheduling can take place on any Groups in this Proposal until it is unlocked again.
 * 
 * ### This needs more working out re the logic and structure which has become messy ### 
 *
 * @author S.N.Fraser
 */
public class GET_PROPOSALImpl extends TransactionImpl {

    /** The path used to identify the Proposal in the OSS database. */
    protected Path proposalPath;

    /** The path used to identify the current User (editor) in the database. */
    protected Path editorPath;

    /** The key to be used to lock the Proposal in the Database. */
    protected int key;

    /** RegistrationID of the client which is retrieving the proposal.*/
    protected long regId;

    /** True if the proposal is to be locked.*/
    protected boolean dolock;

    /** Create a GET_PROPOSALImpl for the specified request.*/
    public GET_PROPOSALImpl(GET_PROPOSAL request) {
	super(request);
	this.proposalPath = request.getProposalPath();
	this.editorPath   = request.getEditorPath();
	this.key          = request.getKey();
	this.regId        = request.getRegId();
	this.dolock       = request.getDolock();
    }

    /**
     * Retrieves the Proposal specified.  
     */
    public TRANSACTION_DONE exec() { 

	myLog = LogManager.getLogger("TRACE");

	Proposal proposal = null;
	
	Transaction t = null;
	try {
	    t = Transaction.current();
	    myLog.log(1, CLASS, id, "exec", 
		      "Fetching proposal: "+proposalPath+
		      " for "+editorPath+", "+
		      (dolock ? "Lock":"NO lock")+" was requested");

	    proposal = null;
	    try {
		proposal = OSS_Utilities.fetchProposal(proposalPath);
		myLog.log(1, CLASS, id, "exec", "Proposal was fetched ok");
	    } catch (IOException iox) {
		myLog.log(1, CLASS, id, "exec",
			   "Proposal: "+proposalPath+
			   " Error fetching Proposal from Database: "+iox);
		return error(TRANSACTION.DATABASE_READ_ERROR,			   
				     "Proposal: "+proposalPath+
				     ": Error reading Proposal from database: "+iox);
	    }		
	    
	    // Check Proposal exists.
	    if (proposal == null) {
		// Couldn't find the Proposal.
		myLog.log(1, CLASS, id, "exec", 
			   "Proposal: "+proposalPath+" Not in database.");
		return error(TRANSACTION.PATH_RESOLUTION_ERROR, 
				     "Could not find Proposal: "+proposalPath);
	    } 


	    // See if its Locked.
	    if (proposal.isLocked()) {	   

		// Someone has already locked this one for editing.
		// See if it has been exported.		
		if (!proposal.isExported()) {
		    myLog.log(1, CLASS, id, "exec", 
			       "Attempt to access locked Proposal: "+
			       proposalPath.toString()+" lock is: "+proposal.getLock());
		    Date date = new Date(proposal.getLastLocked());
		    return error(TRANSACTION.ILLEGAL_DATABASE_ACCESS,
				 "Proposal locked by user: "+
				 proposal.getLastEditor()+ " at: "+date.toGMTString()+
				 " regID: "+proposal.getLastRegId());
		}
		
		// Someone has Exported it, so we can upload.
		myLog.log(1, CLASS, id, "exec", "Proposal is EXPORTED");
		
		// Let other Users know who is editing now.
		if (dolock) {
		    proposal.setLastEditor(editorPath.toString()); 
		    proposal.setLastLocked(System.currentTimeMillis());
		    proposal.setLastRegId(regId);
		    proposal.setExported(false); // Dont let anyone else upload !	       
		    proposal.lock(key); 
		    try {
			myLog.log(1, CLASS, id, "exec", "Storing proposal lock info...");
			OSS_Utilities.storeProposal(proposal);
		    } catch (IOException ix) {
			myLog.log(1, CLASS, id, "exec",
				  "Proposal ["+proposal.getFullPath()+
				  "] Unable to Store: "+ix);
			return error(TRANSACTION.DATABASE_UPDATE_ERROR, 
				     "Proposal ["+proposal.getFullPath()+
				     "] Unable to Store: "+ix);
		    }
		    // Note: Weve saved it to store so we can mess with the object now.
		    // Unlock to free subtree then relock at Proposal level.
		    proposal.forceUnLock(); // who cares what key is	
		    proposal.setLock(key);
		}
	
		GET_PROPOSAL_DONE done = new GET_PROPOSAL_DONE(id);
		done.setSuccessful(true);
		done.setProposal(proposal);
		return done;	    

	    } else {
		// Not locked - check if its locked lower down.
		if (!proposal.canLock()) {			
		    // One or more Groups/Observations are currently being scheduled/updated
		    // by the RCS or some other client. User will have to wait..
		    myLog.log(1, CLASS, id, "exec", 
			      "Proposal subtree locked out at lower level:"+
			       proposalPath.toString());
		    return error(GET_PROPOSAL.LOW_LEVEL_LOCKOUT, 
				 "A lock is held by another client on a subset of this Proposal: "+
				 proposalPath.toString());
		    
		}
		
		// The Proposal is unlocked and is lockable by us.
		// Let other Users know who is editing now.
		if (dolock) {
		    proposal.setLastEditor(editorPath.toString()); 
		    proposal.setLastLocked(System.currentTimeMillis());
		    proposal.setLastRegId(regId);
		    proposal.lock(key); 
		    try {	
			myLog.log(1, CLASS, id, "exec", "Storing proposal lock info...");
			OSS_Utilities.storeProposal(proposal);
		    } catch (IOException ix) {
			myLog.log(1, CLASS, id, "exec",
				  "Proposal ["+proposal.getFullPath()+
				  "] Unable to Store: "+ix);
			return error(TRANSACTION.DATABASE_UPDATE_ERROR, 
				     "Proposal ["+proposal.getFullPath()+
				     "] Unable to Store: "+ix);
		    }
		    // Note: Unlock to free subtree then relock at Proposal level.
		    proposal.forceUnLock();	
		    proposal.setLock(key);
		}

		GET_PROPOSAL_DONE done = new GET_PROPOSAL_DONE(id); 
		done.setSuccessful(true);
		done.setProposal(proposal);
		return done;	    	
	    }	    
	} finally {
	    t.commit();
	}	
    }
    
} // Class Def. [GET_PROPOSALImpl].



